package com.googlecode.restitory.gae.hook;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.restitory.gae.AuthKey;
import com.googlecode.restitory.gae.Request;
import com.googlecode.restitory.gae.RequestJsoner;
import com.googlecode.restitory.gae.Response;
import com.googlecode.restitory.gae.Type;
import com.googlecode.restitory.gae.crypto.CryptoUtil;

public class AuthHook extends AbstractHook {

	public static final String HEADER_KEY = "X-Restitory-Auth";

	private static final Logger LOG = LoggerFactory.getLogger(AuthHook.class);

	@Override
	public Response handle(Request request) {
		String appengine = request.getHeaders().getFirst("X-AppEngine-Cron");
		if ("true".equals(appengine)) {
			LOG.info("authorize gae cron: " + request);
			return getChain().execute(request);
		}

		if (!getAdapter().exists("/_/prepare")) {
			return getChain().execute(request);
		}
		return auth(request);
	}

	private Response auth(Request request) {
		Response ret = authCookie(request);
		if (ret.getCode() != 401) {
			return ret;
		}
		String u = request.getParams().getFirst("u");
		String p = request.getParams().getFirst("p");
		String authorization = request.getHeaders().getFirst("Authorization");
		if (authorization != null) {
			String[] str = CryptoUtil.decodeBasicAuth(authorization);
			u = str[0];
			p = str[1];
		}
		if (u != null) {
			return auth(request, u, p);
		}
		return ret;
	}

	private Response authCookie(Request request) {
		AuthKey k = getCookie(request);
		if (k == null) {
			return unauthorize(request);
		}
		String key = k.getKey();
		String data = k.toDataString();
		Response resp = getService().execute(
				new Request(Type.POST, "/_/crypto/checkSign").addParam("k", "/_/crypto/key/master/public")
						.addParam("s", key).setContentType("text/plain").setContent(data));
		if (resp.getCode() == 520) {
			return unauthorize(request);
		} else if (resp.getCode() != 200) {
			return resp;
		}
		k.setDate(System.currentTimeMillis());
		k = getAdapter().sign("/_/crypto/key/master/private", k.toDataString());
		return authorize(request, k);
	}

	public static AuthKey getCookie(Request request) {
		String cookie = request.getHeaders().getFirst("Cookie");
		if (cookie == null) {
			return null;
		}
		String[] split = cookie.split("=");
		cookie = split[1];
		AuthKey k = AuthKey.read(cookie);
		return k;
	}

	private Response auth(Request request, String u, String p) {
		String password = getAdapter().getJson("/_/u/" + u + "/password");
		if (!password.equals(p)) {
			return unauthorize(request);
		}
		AuthKey k = new AuthKey(u, System.currentTimeMillis(), "restitory", "");
		k = getAdapter().sign("/_/crypto/key/master/private", k.toDataString());
		return authorize(request, k);
	}

	public Response authorize(Request request, AuthKey k) {
		String path = request.getPath();
		LOG.info("authorize: " + request + " " + k.toDataString());
		if (path.equals("/_/auth")) {
			return new Response(request).setCode(200).setContentType("application/json")
					.addHeader("Set-Cookie", HEADER_KEY + "=" + k + "; Path=/").setContent(RequestJsoner.formatJson(k));
		}
		Request cReq = request.copy();
		cReq.getHeaders().set("Cookie", HEADER_KEY + "=" + k);
		return getChain().execute(cReq).setRequest(request);
	}

	public static Response unauthorize(Request request) {
		LOG.info("unauthorize: " + request);
		return new Response(request).setCode(401).addHeader("WWW-Authenticate", "Basic realm=\"AuthRealm\"");
	}

	public Response prepare(Request request) {
		getAdapter().createIfNotExists("/_/u/admin/password", "application/json", "'admin'");
		return getChain().execute(request);
	}

}
